@{
    ViewData["Title"] = "流程引擎小部件集成文档";
}

<h2>@(ViewData["Title"] ?? "流程引擎小部件集成文档")</h2>

<div class="card">
    <div class="card-body">
        <h4 class="card-title">概述</h4>
        <p>本文档详细说明了 Details.cshtml 页面如何集成流程引擎的小部件系统。流程引擎小部件通过 iframe 嵌入方式，为业务页面提供丰富的工作流交互功能。</p>
        
        <div class="alert alert-info">
            <h5>流程引擎小部件的作用和价值：</h5>
            <ul>
                <li><strong>统一的工作流界面</strong>：提供标准化的审批、流程图查看等功能</li>
                <li><strong>松耦合集成</strong>：通过 iframe 方式集成，不影响主业务系统</li>
                <li><strong>实时状态同步</strong>：小部件与流程引擎实时通信，状态变更立即生效</li>
                <li><strong>可重用性</strong>：同一套小部件可在多个业务模块中复用</li>
                <li><strong>独立维护</strong>：小部件系统独立更新，不影响业务系统稳定性</li>
            </ul>
        </div>

        <h5>当前集成的小部件类型：</h5>
        <ul>
            <li><code>ProveNodeUser</code> - 审批节点用户小部件</li>
            <li><code>ProveCard</code> - 审批卡片小部件</li>
            <li><code>FlowDesign</code> - 流程图设计/查看小部件</li>
        </ul>
    </div>
</div>

<div class="card mt-4">
    <div class="card-body">
        <h4 class="card-title">技术实现</h4>
        
        <h5>1. iframe 嵌入方式</h5>
        <p>所有小部件均通过 iframe 标签嵌入到页面中，确保完全隔离和安全性。</p>
        <div class="alert alert-secondary">
            <strong>技术优势：</strong>
            <ul>
                <li>完全隔离：避免 CSS、JavaScript 冲突</li>
                <li>安全性：独立的执行环境</li>
                <li>跨域支持：支持不同域名的小部件服务</li>
                <li>独立加载：小部件错误不影响主页面</li>
            </ul>
        </div>

        <h5>2. 自动 Token 获取机制</h5>
        <p>系统自动从 Cookie 中获取 JWT Token 并注入到小部件 URL 中，实现无缝认证。</p>
        <pre><code>// 核心实现代码
function getCookie(name) {
    const value = `; ${document.cookie}`;
    const parts = value.split(`; ${name}=`);
    if (parts.length === 2) {
        return parts.pop().split(';').shift();
    }
    return null;
}

// 页面加载后自动为iframe补充token
document.addEventListener('DOMContentLoaded', function () {
    var jwt = getCookie('JwtToken');
    if (!jwt) return;
    var token = jwt.replace('Bearer ', '');
    // 更新所有 iframe 的 src
});</code></pre>

        <h5>3. 高度自适应机制</h5>
        <p>通过 PostMessage API 实现 iframe 内容高度的动态调整。</p>
        <div class="alert alert-info">
            <strong>双重高度适应策略：</strong>
            <ul>
                <li><strong>onload 事件</strong>：iframe 加载完成后通过 <code>resizeIframe()</code> 函数尝试获取内容高度</li>
                <li><strong>PostMessage 通信</strong>：小部件主动发送高度信息到父页面</li>
                <li><strong>兜底机制</strong>：跨域限制时的异常处理</li>
            </ul>
        </div>

        <h5>4. 跨域通信处理</h5>
        <p>使用 window.postMessage 实现父页面与 iframe 之间的安全通信。</p>
        <pre><code>// 父页面监听message事件
window.addEventListener('message', function (event) {
    // 刷新事件处理
    if (event.data && event.data.type === 'refresh') {
        location.reload();
        return;
    }
    
    // 高度调整事件处理
    if (event.data && event.data.type === 'resize' && event.data.height) {
        switch (event.data.id) {
            case 'Workflow-ProveCard':
            case 'Workflow-ProveNodeUser':
            case 'Workflow-FlowDesign':
                // 根据不同小部件ID调整对应iframe高度
                break;
        }
    }
}, false);</code></pre>
    </div>
</div>

<div class="card mt-4">
    <div class="card-body">
        <h4 class="card-title">小部件详解</h4>
        
        <h5>1. ProveNodeUser（审批节点用户）小部件</h5>
        <ul>
            <li><strong>功能</strong>：显示当前审批节点的审批人信息</li>
            <li><strong>位置</strong>：页面主要区域，<code>proveNodeUserFrame</code></li>
            <li><strong>URL 格式</strong>：<code>http://localhost:5173/index.html#/components/ProveNodeUser/BusinessDemo/?taskId={WorkTaskId}&access_token={token}</code></li>
            <li><strong>交互特性</strong>：
                <ul>
                    <li>显示审批人头像、姓名、部门等信息</li>
                    <li>支持审批操作（通过、拒绝、退回等）</li>
                    <li>自动高度调整</li>
                </ul>
            </li>
        </ul>

        <h5>2. ProveCard（审批卡片）小部件</h5>
        <ul>
            <li><strong>功能</strong>：显示审批历史记录和当前状态</li>
            <li><strong>位置</strong>：ProveNodeUser 下方，<code>proveCardFrame</code></li>
            <li><strong>URL 格式</strong>：<code>http://localhost:5173/index.html#/components/ProveCard/BusinessDemo/?taskId={WorkTaskId}&access_token={token}</code></li>
            <li><strong>交互特性</strong>：
                <ul>
                    <li>时间线形式展示审批过程</li>
                    <li>显示每个审批节点的状态、审批人、审批时间</li>
                    <li>支持审批意见查看</li>
                </ul>
            </li>
        </ul>

        <h5>3. FlowDesign（流程图设计）小部件</h5>
        <ul>
            <li><strong>功能</strong>：以图形化方式展示工作流程定义</li>
            <li><strong>位置</strong>：模态框中，<code>flowDesignFrame</code></li>
            <li><strong>URL 格式</strong>：<code>http://localhost:5173/index.html#/components/FlowDesign/BusinessDemo/?taskId={WorkTaskId}&access_token={token}</code></li>
            <li><strong>交互特性</strong>：
                <ul>
                    <li>流程图可视化展示</li>
                    <li>节点状态高亮显示</li>
                    <li>支持缩放和拖拽操作</li>
                    <li>固定高度 500px</li>
                </ul>
            </li>
        </ul>
    </div>
</div>

<div class="card mt-4">
    <div class="card-body">
        <h4 class="card-title">集成步骤</h4>
        
        <h5>步骤 1：添加 iframe 容器</h5>
        <pre><code>&lt;iframe id="yourWidgetFrame"
    src="https://localhost:5001/index.html#/components/YourWidget?taskId={WorkTaskId}&access_token="
    width="100%" frameborder="0" allowfullscreen onload="resizeIframe(this)"&gt;&lt;/iframe&gt;</code></pre>

        <h5>步骤 2：配置 JavaScript 自动 Token 注入</h5>
        <pre><code>// 在 frames 数组中添加新的小部件配置
var frames = [
    { id: 'yourWidgetFrame', base: 'http://localhost:5173/index.html#/components/YourWidget/BusinessDemo/?taskId={WorkTaskId}&access_token=' }
    // ... 其他小部件
];</code></pre>

        <h5>步骤 3：添加高度自适应监听</h5>
        <pre><code>// 在 window.addEventListener('message') 中添加新的 case
case 'Workflow-YourWidget':
    {
        var iframe = document.getElementById('yourWidgetFrame');
        if (iframe) {
            iframe.style.height = event.data.height + 'px';
        }
        break;
    }</code></pre>

        <h5>配置参数说明</h5>
        <div class="table-responsive">
            <table class="table table-bordered">
                <thead>
                    <tr>
                        <th>参数</th>
                        <th>说明</th>
                        <th>必填</th>
                        <th>示例</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>taskId</td>
                        <td>工作任务ID，用于获取流程数据</td>
                        <td>是</td>
                        <td>{WorkTaskId}</td>
                    </tr>
                    <tr>
                        <td>access_token</td>
                        <td>JWT认证令牌</td>
                        <td>是</td>
                        <td>自动从Cookie获取</td>
                    </tr>
                    <tr>
                        <td>id</td>
                        <td>iframe元素的唯一标识</td>
                        <td>是</td>
                        <td>proveCardFrame</td>
                    </tr>
                    <tr>
                        <td>base URL</td>
                        <td>小部件的基础访问地址</td>
                        <td>是</td>
                        <td>http://localhost:5173/index.html#/components/</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</div>

<div class="card mt-4">
    <div class="card-body">
        <h4 class="card-title">代码示例</h4>
        
        <h5>完整的 HTML 结构示例</h5>
        <pre><code>&lt;div class="mt-4"&gt;
    &lt;h4&gt;流程相关小部件&lt;/h4&gt;
    &lt;div class="row"&gt;
        &lt;div class="col-md-12 mb-3"&gt;
            &lt;div class="card-body p-2"&gt;
                &lt;!-- 审批节点用户小部件 --&gt;
                &lt;div style="height:auto;"&gt;
                    &lt;iframe id="proveNodeUserFrame"
                        src="https://localhost:5001/index.html#/components/ProveNodeUser?taskId={WorkTaskId}&access_token="
                        width="100%" frameborder="0" allowfullscreen onload="resizeIframe(this)"&gt;&lt;/iframe&gt;
                &lt;/div&gt;
                
                &lt;!-- 审批卡片小部件 --&gt;
                &lt;div class="mb-3" style="height:auto;"&gt;&lt;/div&gt;
                &lt;iframe id="proveCardFrame"
                    src="https://localhost:5001/index.html#/components/ProveCard?taskId={WorkTaskId}&access_token="
                    width="100%" frameborder="0" allowfullscreen onload="resizeIframe(this)"&gt;&lt;/iframe&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    
    &lt;!-- 流程图查看按钮 --&gt;
    &lt;div class="col-md-12 mb-3"&gt;
        &lt;button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#flowDesignModal"&gt;
            查看流程图
        &lt;/button&gt;
    &lt;/div&gt;
&lt;/div&gt;</code></pre>

        <h5>JavaScript 完整实现示例</h5>
        <pre><code>&lt;script&gt;
    // 获取cookie
    function getCookie(name) {
        const value = `; ${document.cookie}`;
        const parts = value.split(`; ${name}=`);
        if (parts.length === 2) {
            return parts.pop().split(';').shift();
        }
        return null;
    }
    
    // 页面加载后自动为iframe补充token
    document.addEventListener('DOMContentLoaded', function () {
        var jwt = getCookie('JwtToken');
        if (!jwt) return;
        var token = jwt.replace('Bearer ', '');
        
        var frames = [
            { id: 'proveCardFrame', base: 'http://localhost:5173/index.html#/components/ProveCard/BusinessDemo/?taskId={WorkTaskId}&access_token=' },
            { id: 'proveNodeUserFrame', base: 'http://localhost:5173/index.html#/components/ProveNodeUser/BusinessDemo/?taskId={WorkTaskId}&access_token=' },
            { id: 'flowDesignFrame', base: 'http://localhost:5173/index.html#/components/FlowDesign/BusinessDemo/?taskId={WorkTaskId}&access_token=' }
        ];
        
        frames.forEach(function (f) {
            var frame = document.getElementById(f.id);
            if (frame) {
                frame.src = f.base + token;
            }
        });
    });
    
    // 使iframe高度自适应其内容
    function resizeIframe(iframe) {
        setTimeout(function () {
            try {
                if (iframe && iframe.contentWindow && iframe.contentWindow.document.body) {
                    iframe.style.height = iframe.contentWindow.document.body.scrollHeight + 'px';
                }
            } catch (e) {
                // 跨域无法自适应时忽略
            }
        }, 300);
    }

    // 父页面监听message，根据id分别设置对应iframe高度
    window.addEventListener('message', function (event) {
        // 刷新事件处理
        if (event.data && event.data.type === 'refresh') {
            location.reload();
            return;
        }
        
        if (event.data && event.data.type === 'resize' && event.data.height) {
            switch (event.data.id) {
                case 'Workflow-ProveCard':
                    {
                        var iframe = document.getElementById('proveCardFrame');
                        if (iframe) {
                            iframe.style.height = event.data.height + 'px';
                        }
                        break;
                    }
                case 'Workflow-ProveNodeUser':
                    {
                        var iframe = document.getElementById('proveNodeUserFrame');
                        if (iframe) {
                            iframe.style.height = event.data.height + 'px';
                        }
                        break;
                    }
                case 'Workflow-FlowDesign':
                    {
                        var iframe = document.getElementById('flowDesignFrame');
                        if (iframe) {
                            iframe.style.height = event.data.height + 'px';
                        }
                        break;
                    }
            }
        }
    }, false);
&lt;/script&gt;</code></pre>
    </div>
</div>

<div class="card mt-4">
    <div class="card-body">
        <h4 class="card-title">常见问题解决</h4>
        
        <h5>Q1: iframe 高度不能自适应？</h5>
        <p><strong>解决方案：</strong></p>
        <ul>
            <li>确保小部件发送了正确的 postMessage 事件</li>
            <li>检查 event.data.id 是否与预期的小部件 ID 匹配</li>
            <li>验证 iframe 的 ID 是否正确</li>
            <li>使用浏览器开发者工具查看 console 输出</li>
        </ul>

        <h5>Q2: Token 获取失败？</h5>
        <p><strong>解决方案：</strong></p>
        <ul>
            <li>检查用户是否已登录，Cookie 中是否存在 JwtToken</li>
            <li>确认 Cookie 的 domain 和 path 设置正确</li>
            <li>检查 getCookie 函数的实现是否正确</li>
        </ul>

        <h5>Q3: 小部件加载缓慢或失败？</h5>
        <p><strong>解决方案：</strong></p>
        <ul>
            <li>检查小部件服务是否正常运行（默认 localhost:5173）</li>
            <li>确认网络连接和防火墙设置</li>
            <li>查看浏览器网络面板的请求状态</li>
            <li>检查跨域设置是否正确</li>
        </ul>

        <h5>Q4: 小部件功能异常？</h5>
        <p><strong>解决方案：</strong></p>
        <ul>
            <li>检查 taskId 参数是否正确传递</li>
            <li>确认工作流状态是否正常</li>
            <li>查看小部件内部的错误日志</li>
            <li>验证用户权限是否足够</li>
        </ul>

        <div class="alert alert-warning">
            <h5>注意事项：</h5>
            <ul>
                <li>确保小部件服务与主应用的时间同步</li>
                <li>定期检查 JWT Token 的有效期</li>
                <li>在生产环境中使用 HTTPS 协议</li>
                <li>合理设置 iframe 的安全策略</li>
            </ul>
        </div>
    </div>
</div>

<div class="alert alert-info mt-4" role="alert">
    <strong>提示：</strong>页面会自动从 Cookie 获取 JwtToken 并填充到小部件 token 参数，无需手动替换。此机制确保了用户身份认证的无缝对接。
</div>

<div class="mt-3">
    <a asp-action="Index" class="btn btn-secondary">返回列表</a>
</div>